/*+**************************************************************************/
/***                                                                      ***/
/***   This file is distributed under a BSD license.                      ***/
/***   See LICENSE.txt for details.                                       ***/
/***                                                                      ***/
/**************************************************************************+*/

/****************************************************************************/
/***                                                                      ***/
/***   (C) 2005 Dierk Ohlerich, all rights reserved                       ***/
/***                                                                      ***/
/****************************************************************************/

asc
{
  cbuffer Julia4DVSPara : register(c0) : slot vs 0
  {
    row_major float4x4 mvp;     // rotation for light vector (not really needed)
    row_major float4x4 Camera;
    float4    UVOffset;
    
    extern void Set(const sViewport &view, const sMatrix34 &cam)
    {
      mvp = view.ModelScreen;
      Camera = cam;
      UVOffset.Init(-0.5f/view.Target.SizeX(),-0.5f/view.Target.SizeY(),0,0);
    }
  };

  cbuffer Julia4DPSPara : register(c0) : slot ps 0
  {
    float4 C;
    float4 Params1; // Cam.k/ZFar
    float4 ClipPlane;
    float4x3 mv;
    
    float4 Color;
    
    float4 ldir_vs;  // w: specular coeff
    float4 lc_front;
    float4 lc_mid;
    float4 lc_back;
    float4 lc_spec;
    float4 ambient;
    
    float4 Fog; // add, mul, density, ?
    float4 FogColor;
  };

  permute Julia4DPSPerm
  {
    ZOnly;
  };    
}

/****************************************************************************/

material Julia4DPass1Mtrl
{
  prepare
  {
    VertexShader = VS();
    PixelShader = PS();
  }
  
  vs
  {
    asc vs_3_0                 // hlsl code
    {
      use Julia4DVSPara;

      void main
      (
        in float3 in_pos : POSITION,
        in float2 in_uv0 : TEXCOORD0,
        out float3 out_eye : TEXCOORD0,
        out float3 out_ray : TEXCOORD1,
        out float4 out_pos : POSITION, 
      )
      {
        out_eye = Camera[3].xyz;
                
        float3 i=Camera[0].xyz;
        float3 j=Camera[1].xyz;
        float3 k=Camera[2].xyz;       
        out_ray = normalize(in_uv0.x*i+in_uv0.y*j+k);
        out_pos = mul(float4(in_pos,1),mvp);
      }
    }
  }

  ps
  {
    cflags dont_optimize;                                                                              
               
    asc ps_3_0 
    {
      use Julia4DPSPara;

      float4 QuatMul( float4 q1, float4 q2 )
      {
        float4 r;
        r.x   = q1.x*q2.x - dot( q1.yzw, q2.yzw );
        r.yzw = q1.x*q2.yzw + q2.x*q1.yzw + cross( q1.yzw, q2.yzw );
        return r;
      }

      float4 QuatSqr( float4 q )
      {
        float4 r;
        r.x   = q.x*q.x - dot( q.yzw, q.yzw );
        r.yzw = 2*q.x*q.yzw;
        return r;
      }
     
      void IterateJulia(inout float4 q, inout float4 qp, float4 c, int maxiter)
      {
        for(int i=0; i<maxiter && dot( q, q ) < 4.0; i++)
        {
          qp = 2.0 * QuatMul(q, qp);
          q = QuatSqr(q) + c;
        }
      }
    
      float GetQJulia( inout float3 pos, float3 ray, float4 c, int maxiter, float epsilon)
      {
        float dist = 0;
        
        do
        {
          float4 z = float4(pos,0);
          float4 zp = float4(1,0,0,0);
          IterateJulia(z, zp, c, maxiter);
          float lz = length(z);
          dist = 0.5*lz*log(lz)/length(zp);
          pos += ray * dist;
        } while (dist >= epsilon && dot(pos,pos) < 3.0);

        return dist;
      }

      float3 TraceBoundingSphere (float3 pos, float3 ray)
      {
         float B = 2 * dot(pos, ray);
         float C = dot(pos,pos) - 3.0;
         float d = sqrt( B*B - 4*C );
         float t0 = ( -B + d ) * 0.5;
         float t1 = ( -B - d ) * 0.5;
         float t = min( t0, t1 );
         clip(t);
         pos += t * ray;
         return pos;
      }
    
      float4 main 
      (
        in float3 eye : TEXCOORD0,
        in float3 ray : TEXCOORD1,
      ) : COLOR0
      {
        float epsilon = 0.001;
        int maxiter = 10;
        
        ray = normalize(ray);
        float3 pos = eye;
        if (dot(eye,eye)>3.0) pos=TraceBoundingSphere(eye, ray);
        float dist = GetQJulia(pos, ray, C, maxiter, epsilon);

        clip (epsilon-dist);
        clip (dot(float4(pos,1),ClipPlane));
        return length(pos-eye);
      }
    }
    
  }
};

/****************************************************************************/

material Julia4DPass2Material
{
  prepare
  {
    VertexShader = VS();
    PixelShader = PS();
  }
  
  vs
  {
    asc vs_3_0                 // hlsl code
    {
      use Julia4DVSPara;

      void main
      (
        in float3 in_pos : POSITION,
        in float2 in_uv0 : TEXCOORD0,
        in float2 in_uv1 : TEXCOORD1,
        out float3 out_eye : TEXCOORD0,
        out float3 out_ray : TEXCOORD1,
        out float2 out_uv  : TEXCOORD2,
        out float4 out_pos : POSITION,
      )
      {
        out_eye = Camera[3].xyz;       
        float3 i=Camera[0].xyz;
        float3 j=Camera[1].xyz;
        float3 k=Camera[2].xyz;       
        out_ray = normalize(in_uv0.x*i+in_uv0.y*j+k);
        out_pos = mul(float4(in_pos,1),mvp);
        out_uv = in_uv1-UVOffset.xy;
      }
    }
  }

  ps
  {
    asc ps_3_0 
    {
      use Julia4DPSPara;

      sampler2D depthtex : register(s0);
      
      float4 QuatSqr( float4 q )
      {
        float4 r;
        r.x   = q.x*q.x - dot( q.yzw, q.yzw );
        r.yzw = 2*q.x*q.yzw;
        return r;
      }
     
      float3 GetJuliaNormal(float3 pos, float4 c, int maxiter)
      {
        float4 z = float4(pos,0);

        float  delta = 0.0001;
        float4 gx1 = z - float4( delta, 0, 0, 0 );
        float4 gx2 = z + float4( delta, 0, 0, 0 );
        float4 gy1 = z - float4( 0, delta, 0, 0 );
        float4 gy2 = z + float4( 0, delta, 0, 0 );
        float4 gz1 = z - float4( 0, 0, delta, 0 );
        float4 gz2 = z + float4( 0, 0, delta, 0 );

        for(int i=0; i<maxiter; i++)
        {
          gx1 = QuatSqr(gx1)+c;
          gx2 = QuatSqr(gx2)+c;
          gy1 = QuatSqr(gy1)+c;
          gy2 = QuatSqr(gy2)+c;
          gz1 = QuatSqr(gz1)+c;
          gz2 = QuatSqr(gz2)+c;
        }

        float3 norm;          
        norm.x = length(gx2) - length(gx1);
        norm.y = length(gy2) - length(gy1);
        norm.z = length(gz2) - length(gz1);
        return norm;
      }
    
      float4 main 
      (
        in float3 eye  : TEXCOORD0,
        in float3 ray  : TEXCOORD1,
        in float2 uv   : TEXCOORD2,
      ) : COLOR0
      {
        int maxiter = 10;

        float depth = tex2D(depthtex,uv);
        clip(depth-0.1);
               
        ray = normalize(ray);
        float3 pos = eye+depth*ray;
        float3 norm = GetJuliaNormal(pos,C,maxiter);       
        
        norm=normalize(mul(norm,mv));
        
        //float depth = ;
        float z = dot(pos-eye,Params1.xyz);
        
        return float4(z,norm);
      }
    }
    
  }
};

/****************************************************************************/

material Julia4DPass3Material
{
  prepare
  {
    VertexShader = VS();
    PixelShader = PS();
  }
  
  vs
  {
    asc vs_3_0                 // hlsl code
    {
      use Julia4DVSPara;

      void main
      (
        in float3 in_pos : POSITION,
        in float2 in_uv0 : TEXCOORD0,
        in float2 in_uv1 : TEXCOORD1,
        out float3 out_eye : TEXCOORD0,
        out float3 out_ray : TEXCOORD1,
        out float2 out_uvd : TEXCOORD2,
        out float4 out_pos : POSITION,
      )
      {
        out_eye =Camera[3].xyz;       
        float3 i=Camera[0].xyz;
        float3 j=Camera[1].xyz;
        float3 k=Camera[2].xyz;       
        out_ray = normalize(in_uv0.x*i+in_uv0.y*j+k);
        out_pos = mul(float4(in_pos,1),mvp);
        out_uvd = in_uv1-UVOffset.xy;
      }
    }
  }

  ps
  {
    asc ps_3_0 
    {
      use Julia4DPSPara;

      sampler2D depthtex : register(s0);
      sampler2D normtex : register(s1);
          
      float4 main 
      (
        in float3 eye : TEXCOORD0,
        in float3 ray : TEXCOORD1,
        in float2 uvd : TEXCOORD2,
      ) : COLOR0
      {
        int maxiter = 10;

        float depth = tex2D(depthtex,uvd);
        clip (depth-0.1);

        ray = normalize(ray);
        float3 pos = eye+depth*ray;

        float4 norm = tex2D(normtex,uvd).yzwx;
        norm.w=0;
        
        float3 col = ambient;       
        float ndotl = dot(norm.xyz,ldir_vs.xyz);
        
        if (ndotl>=0)
          col += lerp(lc_mid,lc_front,ndotl);
        else
          col += lerp(lc_mid,lc_back,-ndotl);
        
        float3 refl = normalize(reflect(mul(ray,mv),norm.xyz));
        float rdot = dot(refl,ldir_vs.xyz);
        float spec = pow(max(0,rdot),ldir_vs.w);
        
        col = col*Color+lc_spec*spec;
        
        float fog=saturate(Fog.y*(2*depth+Fog.x));
        fog = 2*fog-fog*fog;
        fog = fog * Fog.z;

        return float4(lerp(col,FogColor,fog),1); 
       
      }
    }
    
  }
};

/****************************************************************************





      float ball(float3 p, float3 center, float r)
      {
        return length(p-center)-r;
      }

      float distance(float3 p)
      {
        float d1 = ball(p,float3(-2,1.5,0),1);
        float d2 = ball(p,float3( 0,1.5,0),1.5);
        float d3 = ball(p,float3( 2.5,1,0),1);
        
        float d4 = p.y;
        
        return min(min(d1,d4),min(d2,d3));
      }
      
      float3 getnormal(float3 pos, float orgdist)
      {
        float normwidth = 0.001;
        float3 d3;
        d3.x = distance(pos+float3(normwidth,0,0));
        d3.y = distance(pos+float3(0,normwidth,0));
        d3.z = distance(pos+float3(0,0,normwidth));
        return normalize(d3-orgdist);
      }
      
      float getocclusion(float3 pos, float3 normal)
      {
        float ao;
        float totao = 0.0;
        float sca = 10.0;
        for( int aoi=0; aoi<5; aoi++ )
        {
          float hr = 0.01 + 0.015*float(aoi*aoi);
          float3 aopos =  normal * hr + pos;
          float dd = distance(aopos);
          ao = -(dd-hr);
          totao += ao*sca;
          sca *= 0.5;
        }
        return 1.0 - saturate(totao);
      }


        float3 pos = eye;
        float dist = distance(pos);
        while (length(pos)<10 && dist>epsilon)
        {
          pos += ray*dist;
          dist = distance(pos);
        } 

        if (dist<=epsilon)
        {
          if (pos.y>epsilon)
          {     
            float3 norm = GetJuliaNormal(pos,constant,maxiter);
            float3 amb = float3(1,1,1);
            float3 color = float3(1,0.75,0.5);
            amb *= max(0,(norm.y+3)/4);            
            //color*=amb*getocclusion(pos,norm);
            
            return float4(color,1);
          }
          else
          {
            // AO on ground plane
            return float4(0,0,0,1.0-getocclusion(pos,float3(0,1,0)));
          }
        }

*****************************************************************************/
